package naga.x.net.impl.pomelo.protocol;

import org.apache.mina.core.buffer.IoBuffer;

import naga.x.net.impl.pomelo.PomeloException;
import naga.x.util.IOUtils;
import naga.x.util.NumberUtils;
import naga.x.util.StringUtils;

public class Package {

	public static final int TYPE_HANDSHAKE = 1;
	public static final int TYPE_HANDSHAKE_ACK = 2;
	public static final int TYPE_HEARTBEAT = 3;
	public static final int TYPE_DATA = 4;
	public static final int TYPE_KICK = 5;
	
	public static final int PKG_HEAD_BYTES = 4;
	
	private int type;
	private byte[] body;
	
	public Package(int type) {
		this(type, null);
	}
	
	public Package(int type, byte[] body) {
		this.type = type;
		this.body = body;
	}
	
	public int getType() {
		return type;
	}

	public byte[] getBody() {
		return body;
	}

	/**
	 * <pre>
	 * Package protocol encode.
	 *
	 * Pomelo package format:
	 * +------+-------------+------------------+
	 * | type | body length |       body       |
	 * +------+-------------+------------------+
	 *
	 * Head: 4bytes
	 *   0: package type,
	 *      1 - handshake,
	 *      2 - handshake ack,
	 *      3 - heartbeat,
	 *      4 - data
	 *      5 - kick
	 *   1 - 3: big-endian body length
	 * Body: body length bytes
	 * </pre>
	 *
	 * @param  {Number}    type   package type
	 * @param  {ByteArray} body   body content in bytes
	 * @return {ByteArray}        new byte array that contains encode result
	 */
	public static byte[] encode(int type, byte[] body) {
		int length = body != null ? body.length : 0;
	    byte[] buffer = new byte[PKG_HEAD_BYTES + length];
	    int index = 0;
	    buffer[index++] = (byte) (type & 0xff);
	    buffer[index++] = (byte) ((length >> 16) & 0xff);
	    buffer[index++] = (byte) ((length >> 8) & 0xff);
	    buffer[index++] = (byte) (length & 0xff);
	    if (body != null) {
	    	System.arraycopy(body, 0, buffer, index, length);
	    }
	    return buffer;
	}
	
	public static byte[] encode(int type) {
		return encode(type, NumberUtils.EMPTY_BYTES);
	}
	
	public static byte[] encode(int type, Message message) {
		try {
			return encode(type, Message.encode(message));
		} catch (PomeloException e) {
		}
		return null;
	}
	
	public static byte[] encode(Package pkg) {
		return encode(pkg.type, pkg.body);
	}
	
	public static byte[] encode(int type, String body) {
		return encode(type, StringUtils.stringToBytes(body, IOUtils.UTF8));
	}
	
	/**
	 * Package protocol decode.<br/>
	 * See encode for package format.
	 * @param {ByteArray} buffer byte array containing package content
	 * @return {Object} {type: package type, buffer: body byte array}
	 */
	public static Package decode(byte[] buffer) {
		return decode(IoBuffer.wrap(buffer));
	}
	
	public static Package decode(IoBuffer buffer) {
		int length = 0;
		int type = buffer.get();
		length = (((buffer.get() & 0xff) << 16) | ((buffer.get() & 0xff) << 8) | (buffer.get() & 0xff)) >>> 0;
		byte[] body = new byte[length > 0 ? length : 0];
		buffer.get(body);
		return new Package(type, body);
	}
	
}
